﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


using SpiritEngine.Source.ApplicationLayer;
using SpiritEngine.Source.ApplicationLayer.Services;
using SpiritEngine.Source.ApplicationLayer.Services.FileIO;
using SpiritEngine.Source.EngineCore;
using SpiritEngine.Source.Utilities;


namespace SpiritEngine.Source.ApplicationLayer
{	
    #region Globals

	#endregion


    /// <summary>
    /// This class represents the Application Layer of the game engine.  It houses all of the base level stuff, such as File I/O, Controller input, etc.
    /// </summary>
    public class EngineBase: SE_Base, ISE_EngineBase
    {
        #region Constants/Enums

        #endregion


        #region Structs

        #endregion


        #region Static Member Variables and Methods

        #endregion


        #region Member Variables

        // TODO: Make engine log command line args into the console.

        // The Engine Core.  This is effectively the Engine itself.  It is the software layer that sits atop the Application Layer (represented by this class: EngineBase).
        ISE_EngineCore m_EngineCore;

        string[] m_CommandLineArgs; // Stores passed in command line arguments.


        // ========================================================================================================================================================================================================
        // Services
        // ========================================================================================================================================================================================================
        internal ISE_ServicesManager m_ServicesManager;

        ISE_FileIO_Service m_FileIO_Service;
        // ========================================================================================================================================================================================================


        // ========================================================================================================================================================================================================
        // Property Variables
        // ========================================================================================================================================================================================================

        // These are private to force classes that inherit from this class to declare their own member variable for use by the corresponding properties.
        // This way, for example, we can keep track of initialization state for each step in the inheritence chain.  So if the base class is initialized, but
        // the subclass is not, then the base class's IsInitialized property would return true, but the subclass's IsInitialized property would return false.
        private int m_SEObjectFlags = 0;    // Stores object flags defined by the SEObject_Flags enum.


        
        #endregion


        #region Events/Delegates

        #endregion


        #region Constructors and Destructors (aka Finalizers)

        /// <summary>
        /// This constructor allows the engine to be started without any command line arguments.
        /// See the InitEngine() function for the engine intialization code.  The initialization code
        /// is not here in the constructor because it is a bad idea to put code that could fail into
        /// a constuctor.  This is because constructors cannot return a value.  Also, if code in the
        /// constructor fails and an exception is thrown, then the constructor simply aborts and
        /// you are left with an invalid object instance that was only partially constructed if
        /// the program tries to keep running.
        /// </summary>
        public EngineBase()
        {
            m_CommandLineArgs = null;
        }

        /// <summary>
        /// This constructor allows you to pass in command line arguments.
        /// See the InitEngine() function for the engine initialization code.  The initialization code
        /// is not here in the constructor because it is a bad idea to put code that could fail into
        /// a constuctor.  This is because constructors cannot return a value.  Also, if code in the
        /// constructor fails and an exception is thrown, then the constructor simply aborts and
        /// you are left with an invalid object instance that was only partially constructed if
        /// the program tries to keep running.
        /// </summary>
        /// <param name="args"></param>
        public EngineBase(string[] args)
        {
            // TODO: Add a function to parse the command line args.
            // Store the command line args in a member variable so they can be processed a bit later.
            m_CommandLineArgs = args;
        }

        #endregion


        #region Non-Public Methods

        // This section is for methods that are declared as private, protected, protected internal, or internal.

        #endregion


        #region Public Methods

        /// <summary>
        /// This function initializes the engine.
        /// This gives us more control than using the contructor, which has no return type, and so it can't return an error code for example.
        /// Also, if we put code that could fail in the constructor, and that code does fail, then the class is not fully initialized, and
        /// so we have an invalid instance.  So this approach makes it much easier to catch errors than using the constructor would.
        /// </summary>
        /// <param name="paramList">A variable length parameter list.
        /// 
        /// EXPECTED PARAMETERS:
        /// None</param>
        /// <returns>True if successful or false otherwise.</returns>
        /// <exception cref="System.Exception"></exception>
        public override bool Initialize(params object[] paramsList)
        {

            base.Initialize();


#if VERIFY_PARAMS
            if (!SE_Debug.VerifyParameters(paramsList))
            {
                
                throw new ArgumentException("EngineBase.Initialize(object[]): Invalid params!");
            }
#endif




            // ========================================================================================================================================================================================================
            // OS-Independent Code
            // ========================================================================================================================================================================================================


            // Initialize Components
            m_EngineCore.Initialize();
            m_ServicesManager.Initialize();


            // Set the Initialized flag to true to indicate that this object has been initialized.
            m_SEObjectFlags = SE_Misc.Flags_SetFlags(m_SEObjectFlags, (int)SEObject_Flags.Initialized);

            return true;
        }


        /// <summary>
        /// Gets a reference to the EngineCore component.
        /// </summary>
        /// <returns>Returns the reference.</returns>
        public ISE_EngineCore GetComponent_EngineCore()
        {
            return m_EngineCore;
        }


        /// <summary>
        /// Gets a reference to the ServicesManager component.
        /// </summary>
        /// <returns>Returns the reference.</returns>
        public ISE_ServicesManager GetComponent_ServicesManager()
        {
            return m_ServicesManager;
        }


        /// <summary>
        /// Gets a reference to the FileIO service.
        /// 
        /// This method is a convenience method allowing you to access one of the most common services without going through the ServicesManager to do it.
        /// </summary>
        /// <returns>Returns the reference.</returns>
        public ISE_FileIO_Service GetService_FileIO()
        {
            return (ISE_FileIO_Service) m_ServicesManager.GetService(typeof(ISE_FileIO_Service));
        }


        /// <summary>
        /// Sets the EngineCore component and returns the old EngineCore component or null if there isn't one.
        /// </summary>
        /// <param name="engineCore">The new EngineCore component to set.</param>
        /// <returns>The old EngineCore component or null if there isn't one.</returns>
        public ISE_EngineCore SetComponent_EngineCore(ISE_EngineCore engineCore)
        {
            ISE_EngineCore old = m_EngineCore;

            m_EngineCore = engineCore;

            return old;
        }


        /// <summary>
        /// Sets the ServicesManager component and returns the old ServicesManager component or null if there isn't one.
        /// </summary>
        /// <param name="manager">The new ServicesManager component to set.</param>
        /// <returns>The old ServicesManager component or null if there isn't one.</returns>
        public ISE_ServicesManager SetComponent_ServicesManager(ISE_ServicesManager manager)
        {
            ISE_ServicesManager old = m_ServicesManager;

            m_ServicesManager = manager;

            return old;
        }


        /// <summary>
        /// Sets the FileIO service and returns the old one, or null if there isn't already a FileIO service.
        /// </summary>
        /// <param name="fileIO_Service">The new FileIO service to set.</param>
        /// <returns>Returns the old FileIO service if there is one, or null otherwise.</returns>
        public ISE_FileIO_Service SetService_FileIO(ISE_FileIO_Service fileIO_Service)
        {
            return (ISE_FileIO_Service)m_ServicesManager.ReplaceService(typeof(ISE_FileIO_Service), fileIO_Service);
        }


        /// <summary>
        /// This function shuts down the engine.
        /// This gives us more control than using the decontructor, which has no return type, and so it can't return an error code for example.
        /// So this approach makes it much easier to catch errors than using the deconstructor would.
        /// </summary>
        public void Shutdown()
        {
            // REMEMBER: Always shut down systems in the reverse order that they were initialized in!


            // Check the OS.
#if OS_WINDOWS

            // Windows-Specific shutdown code
            

#elif OS_ANOTHEROS

            // Shutdown code for this OS.

#elif OS_ANOTHEROS

            // Shutdown code for this OS.
#else
#error Unsupported OS!
#endif

            // ========================================================================================================================================================================================================
            // OS-Independent Code
            // ========================================================================================================================================================================================================

            this.Dispose();

        }


        #endregion


        #region Interface Methods

        // This section is for methods that are part of the interfaces that the class implements.

        #region IDisposable Methods

#if USE_IDISPOSABLE

        /// <summary>
        /// This function handles the actual "disposing" of this object.
        /// ANY classes that are inherited from this class, no matter how distantly, MUST override this method so that they can perform their own cleanup!
        /// </summary>
        /// <param name="disposing">This parameter will be true if this function is being called by the public Dispose() method, or false otherwise.</param>
        protected override void Dispose(bool disposing)
        {
            // Check if this object has already been disposed.
            if (SE_Misc.Flags_HasFlags(m_SEObjectFlags, (int)SEObject_Flags.Disposed))
                return;



            if (disposing)
            {
                /*
                * The following text is from MSDN  (http://msdn.microsoft.com/en-us/library/fs2xkftw%28VS.80%29.aspx)
                * 
                * 
                * Dispose(bool disposing) executes in two distinct scenarios:
                * 
                * If disposing equals true, the method has been called directly or indirectly by a user's code and managed and unmanaged resources can be disposed.
                * If disposing equals false, the method has been called by the runtime from inside the finalizer and only unmanaged resources can be disposed. 
                * 
                * When an object is executing its finalization code, it should not reference other objects, because finalizers do not execute in any particular order. 
                * If an executing finalizer references another object that has already been finalized, the executing finalizer will fail.
                */


                // Unregister Events


                // Clean up all managed resources.
                if (m_EngineCore != null)
                {
                    m_EngineCore.Dispose();
                }

                if (m_ServicesManager != null)
                {
                    m_ServicesManager.Dispose();
                }
            }

            // Clean up all unmanaged resources.



            // Call the base class's Dispose() method if it has one.
            base.Dispose(disposing);
        }

#endif

        #endregion

        #endregion


        #region Event Invocation Methods

        // This section is for methods that invoke the events of this class.  For example:
        //
        // Invoke the Changed event; called whenever list changes
        // protected virtual void OnChanged(EventArgs e) 
        // {
        //		if (Changed != null)
        //			Changed(this, e);
        // }

        #endregion


        #region Operators

        #endregion


        #region Debug Methods

        // This will compile the debug code only if the DEBUG symbol is defined.
        // To define this symbol for all source files in the project, open the Project Properties,
        // and go to the Build tab.  Check the "Define DEBUG constant" checkbox.  With this box checked,
        // the compiler will automatically define the symbol DEBUG.
#if (DEBUG)
        // put all debug methods in here...
#endif

        #endregion


        #region Properties

        /// <summary>
        /// Provides access to the object's "Spirit Engine Object" flags.
        /// </summary>
        public override int SEObjectFlags
        {
            get
            {
                return m_SEObjectFlags;
            }
            protected set // Can only be set from within this class and subclasses
            {
                m_SEObjectFlags = value;
            }
        }


        #endregion

    }
}
